﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom.Compiler;
using System.Reflection;
using System.ComponentModel;
using ObjectBakery.Helpers;
using System.Security.Permissions;
using System.Security.Policy;
using System.Runtime.Serialization;
using System.IO;
using System.Text.RegularExpressions;

namespace ObjectBakery
{

    public enum Language
    {
        CSharp,
        VisualBasic,
        JScript
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
    public class ScriptObjectFactory : Component
    {
        AppDomain appDomain;
        CodeDomProvider compiler;
        CompilerParameters parameters;
        CompilerResults results;
        Language language;

        public ScriptObjectFactory(Language language)
        {
            this.language = language;

            //initialize settings for wrapper AppDomain.
            AppDomainSetup appDomainSetup = new AppDomainSetup();
            appDomainSetup.ApplicationBase = Environment.CurrentDirectory;
            appDomainSetup.DisallowBindingRedirects = false;
            appDomainSetup.DisallowCodeDownload = true;
            appDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

            // Create another AppDomain.
            appDomain = AppDomain.CreateDomain("AppDomain ID: " + Guid.NewGuid().ToString(), null, appDomainSetup);


            compiler = CodeDomProvider.CreateProvider(language.ToString());
            parameters = new CompilerParameters();
            results = null;




            //TODO: Parse strSource for strFullyQualifiedTypes


            parameters.ReferencedAssemblies.Add("mscorlib.dll");
            parameters.ReferencedAssemblies.Add("System.dll");
            //System.Runtime.Serializaton
            parameters.ReferencedAssemblies.Add(typeof(DataContractAttribute).Assembly.Location);

            //parameters.ReferencedAssemblies.Add(typeof(Script).Assembly.Location);

            //parameters.ReferencedAssemblies.Add(Type.GetType(strFullyQualifiedType).Assembly.Location);

            parameters.CompilerOptions = "/t:library";
            parameters.GenerateInMemory = true;

        }

        public IList<object> Build(List<string> typeNames, string strSource)
        {
            List<object> objects = new List<object>();

            results = compiler.CompileAssemblyFromSource(parameters, strSource);

            if (results.Errors.Count > 0)
            {
                StringBuilder sb = new StringBuilder();
                foreach (CompilerError error in results.Errors)
                {
                    sb.AppendLine(error.ErrorText);
                }
                throw new Exception("Errors compiling your assembly:  " + sb.ToString());
            }



            //TODO: Load the assembly in the temp AppDomain


            // return appDomain.GetData("instance");

            // Assembly assembly = results.CompiledAssembly;
            //return Activator.CreateInstance(Type.GetType(strFullyQualifiedType));    


            typeNames.ForEach(strFullyQualifiedType =>
                {
                    objects.Add(results.CompiledAssembly.CreateInstance(strFullyQualifiedType));


                    //var assBuilder = appDomain.DefineDynamicAssembly(new AssemblyName("Test"), System.Reflection.Emit.AssemblyBuilderAccess.RunAndSave);
                    //assBuilder.Save("Test.dll");
                    //byte[] assemblyBytes = new SerializationHelper().BinarySerialize(Assembly.LoadFrom("Test.dll"));


                    //objects.Add(appDomain.CreateInstance("ObjectBakery.Tests", strFullyQualifiedType));

                    //appDomain.SetData("instance", null);
                });

            return objects;
        }





        protected override void Dispose(bool disposing)
        {
            try
            {
                AppDomain.Unload(appDomain);
            }
            catch { }
            base.Dispose(disposing);
        }

        
    }


}